/**
 *  @file FuncPtrT.h
 */
#ifndef _FUNCPTRT_H_
#define _FUNCPTRT_H_

#include "../common/BaseObject.h"
#include "../common/CppMemPool.h"
#include "../common/ObjPtrT.h"
#include "../exceptions/RuntimeException.h"
#include "FuncPtrHelper.h"

namespace cppflib
{

namespace util
{



/**
 *     Function pointer class hierarchy
 *
 *             BaseObject
 *                 /|\
 *                  |
 *             BaseFuncPtr
 *                 /|\
 *                  |
 *         +--------+----------+
 *         |                   |
 *         |                   |
 *     FuncPtr             MemberFuncPtr
 *
 */


/**
 *  Base class for function pointer
 */
template <typename R>
class BaseFuncPtr0T : public BaseObject
{
protected:
   BaseFuncPtr0T() { }
public:
   virtual ~BaseFuncPtr0T() { }
   virtual R operator()() const = 0;  // derived class override operator()
};

/**
 *  Define a macro in order to declare BaseFuncPtr template classes
 *
 *  R is return type
 *  The rest (A1, A2, ..., A10) are parameter types
 */
#define DeclareBaseFuncPtrClass(argNum) \
template <typename R, ARGTYPE_##argNum> \
class BaseFuncPtr##argNum##T : public BaseObject \
{ \
protected: \
   BaseFuncPtr##argNum##T() { } \
public: \
   virtual ~BaseFuncPtr##argNum##T() { } \
   virtual R operator()(ARGP_##argNum) const = 0; \
}

DeclareBaseFuncPtrClass(1);    // class BaseFuncPtr1T
DeclareBaseFuncPtrClass(2);    // class BaseFuncPtr2T
DeclareBaseFuncPtrClass(3);    // class BaseFuncPtr3T
DeclareBaseFuncPtrClass(4);    // class BaseFuncPtr4T
DeclareBaseFuncPtrClass(5);    // class BaseFuncPtr5T
DeclareBaseFuncPtrClass(6);    // class BaseFuncPtr6T
DeclareBaseFuncPtrClass(7);    // class BaseFuncPtr7T
DeclareBaseFuncPtrClass(8);    // class BaseFuncPtr8T
DeclareBaseFuncPtrClass(9);    // class BaseFuncPtr9T
DeclareBaseFuncPtrClass(10);   // class BaseFuncPtr10T



/**
 *  Function pointer to a normal C function with 0 arguments
 */
template <typename R>
class FuncPtr0T : public BaseFuncPtr0T<R>
{
private:
   R (*funcptr)();
private: 
   FuncPtr0T() { }
public:
   FuncPtr0T(const FuncPtr0T<R> &f) { this->funcptr = f.funcptr; }
   explicit FuncPtr0T(R (*funcptr)()) { this->funcptr = funcptr; }
   virtual ~FuncPtr0T() { }
   virtual R operator()() const { return funcptr(); }
};

/**
 *  Define a macro in order to declare FuncPtr template classes
 *
 *  R is return type
 *  The rest (A1, A2, ..., A10) are parameter types
 */
#define DeclareFuncPtrClass(argNum) \
template <typename R, ARGTYPE_##argNum> \
class FuncPtr##argNum##T : public BaseFuncPtr##argNum##T<R, ARGDECL_##argNum> \
{ \
private: \
   R (*funcptr) (ARGDECL_##argNum); \
private: \
   FuncPtr##argNum##T() { } \
public: \
   FuncPtr##argNum##T(const FuncPtr##argNum##T<R, ARGDECL_##argNum> &f) { this->funcptr = f.funcptr; } \
   explicit FuncPtr##argNum##T(R (*funcptr) (ARGDECL_##argNum)) { this->funcptr = funcptr; } \
   virtual ~FuncPtr##argNum##T() { } \
   virtual R operator() (ARGP_##argNum) const { return funcptr (ARGA_##argNum); } \
}

/**
 *  Function pointer classes to a normal C function which take 1 - 10 argument(s)
 */
DeclareFuncPtrClass(1);   // class FuncPtr1T
DeclareFuncPtrClass(2);   // class FuncPtr2T
DeclareFuncPtrClass(3);   // class FuncPtr3T
DeclareFuncPtrClass(4);   // class FuncPtr4T
DeclareFuncPtrClass(5);   // class FuncPtr5T
DeclareFuncPtrClass(6);   // class FuncPtr6T
DeclareFuncPtrClass(7);   // class FuncPtr7T
DeclareFuncPtrClass(8);   // class FuncPtr8T
DeclareFuncPtrClass(9);   // class FuncPtr9T
DeclareFuncPtrClass(10);  // class FuncPtr10T


/**
 *  Function pointer to member function with 0 arguments
 */
template <typename CLASSTYPE, typename R>
class MemberFuncPtr0T : public BaseFuncPtr0T<R>
{
private:
   R (CLASSTYPE::*funcptr)();
   ReferenceT<CLASSTYPE> *pRef;
private:
   MemberFuncPtr0T() { }
public:
   MemberFuncPtr0T(const MemberFuncPtr0T<CLASSTYPE, R> &mf)
   {
      this->pRef = mf.pRef;
      cppmempool::Retain(this->pRef);
      this->funcptr = mf.funcptr;
   }
   MemberFuncPtr0T(CLASSTYPE *pObj, R (CLASSTYPE::*funcptr)(), bool isWeakObj = false)
   {
      if (isWeakObj)
         this->pRef = new WeakReferenceT<CLASSTYPE>(pObj);
      else
         this->pRef = new ReferenceT<CLASSTYPE>(pObj);
      cppmempool::Retain(this->pRef);
      this->funcptr = funcptr;
   }
   virtual ~MemberFuncPtr0T() { cppmempool::Release(this->pRef); }
   virtual R operator()() const 
   { 
      CLASSTYPE * p = pRef->Ptr();
      if (!p)
         throw exceptions::RuntimeException(_S("Reference to object pointer not present"));
      return (*p.*funcptr)(); 
   }
};

/**
 *  Define a macro in order to declare MemberFuncPtr template classes
 *
 *  CLASSTYPE is type of class
 *  R is return type
 *  The rest (A1, A2, ..., A10) are parameter types
 */
#define DeclareMemberFuncPtrClass(argNum) \
template <typename CLASSTYPE, typename R, ARGTYPE_##argNum> \
class MemberFuncPtr##argNum##T : public BaseFuncPtr##argNum##T<R, ARGDECL_##argNum> \
{ \
private: \
   R (CLASSTYPE::*funcptr)(ARGDECL_##argNum); \
   ReferenceT<CLASSTYPE> *pRef; \
private: \
   MemberFuncPtr##argNum##T() { } \
public: \
   MemberFuncPtr##argNum##T(const MemberFuncPtr##argNum##T<CLASSTYPE, R, ARGDECL_##argNum> &mf) \
   { \
      this->pRef = mf.pRef; \
      cppmempool::Retain(this->pRef); \
      this->funcptr = mf.funcptr; \
   } \
   MemberFuncPtr##argNum##T(CLASSTYPE *pObj, R (CLASSTYPE::*funcptr)(ARGDECL_##argNum), bool isWeakObj = false) \
   { \
      if (isWeakObj) \
         this->pRef = new WeakReferenceT<CLASSTYPE>(pObj); \
      else \
         this->pRef = new ReferenceT<CLASSTYPE>(pObj); \
      cppmempool::Retain(this->pRef); \
      this->funcptr = funcptr; \
   } \
   virtual ~MemberFuncPtr##argNum##T() { cppmempool::Release(this->pRef); } \
   virtual R operator()(ARGP_##argNum) const \
   { \
      CLASSTYPE * p = pRef->Ptr(); \
      if (!p) \
         throw exceptions::RuntimeException(_S("Reference to object pointer not present")); \
      return (*p.*funcptr)(ARGA_##argNum); \
   } \
}

/**
 *  Function pointer classes which take 1 - 10 argument(s)
 */
DeclareMemberFuncPtrClass(1);   // class MemberFuncPtr1T
DeclareMemberFuncPtrClass(2);   // class MemberFuncPtr2T
DeclareMemberFuncPtrClass(3);   // class MemberFuncPtr3T
DeclareMemberFuncPtrClass(4);   // class MemberFuncPtr4T
DeclareMemberFuncPtrClass(5);   // class MemberFuncPtr5T
DeclareMemberFuncPtrClass(6);   // class MemberFuncPtr6T
DeclareMemberFuncPtrClass(7);   // class MemberFuncPtr7T
DeclareMemberFuncPtrClass(8);   // class MemberFuncPtr8T
DeclareMemberFuncPtrClass(9);   // class MemberFuncPtr9T
DeclareMemberFuncPtrClass(10);  // class MemberFuncPtr10T

} // end of namespace util

} // end of namespace cppflib

#endif
